/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

package lime2;
import java.util.*;

/**
 * A thread pool extended with input buffers. See class <i>IncomingBuffer</i>. It also deals with assigning incoming 
 * buffers to local op managers.   
 *   
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

public class BufferedThreadPool extends ThreadPool
{
 // hashtable[String,IncomingBuffer]: each thread of the pool has its own input buffer; please note that 
 // java.util.Hashtable is internally synchronized 
 private Hashtable buffers;

 // .. for managing buffers associated to LocalOpMgrs (they do not shrink)
 private static Hashtable lom_buffers = new Hashtable(); // hashtable[String=conv_counter+conv_value,IncomingBuffer]
 private static Hashtable lom_map = new Hashtable(); // hashtable[String=thread_name, String=conv_counter+conv_value]
 private static byte conv_value = 100; // --> no more than 100 worker threads (!), no more than 155 tuplespaces (!)
 private static byte conv_counter = 0;
 
 /**
  * Constructor method.
  * 
  * @param m number of threads this pool must manages
  */
 public BufferedThreadPool(int m)
 {
  super(m);	
 }
 
 /**
  * Initializes the input buffers structure: this method is invoked by the constructor of the thread pool before 
  * starting the threads (no need of synchronization). 
  *
  * @param m number of threads the pool has to manage
  */
 void setUp(int m)
 {
  buffers = new Hashtable();
  for (int i = 0; i < m; i++) buffers.put(String.valueOf(i), new IncomingBuffer());	
 }
 
 /** 
  * Changes the input buffers structure because of an "increasing" resize of the thread pool. This method doesn't need
  * to be synchronized since it is invoked within method "resize" which is synchronized.
  *
  * @param m the new number of threads this pool will manage
  */
 void resizeUp(int m)
 {
  for (int i = poolSize(); i < m; i++) buffers.put(String.valueOf(i), new IncomingBuffer()); 	
 }
 
 /**
  * Performs clean up of the input buffers structure because of the death of a thread (.. "decreasing" resize of the 
  * thread pool). This method is synchronized since multiple threads can die at the "same time" (causing a bad 
  * interleaving).
  * 
  * @param thread name of the dead thread
  */
 synchronized void cleanUp(String thread)
 {
  buffers.remove(thread);
 }
 
 /**
  * Returns the input buffer of a specific thread of the pool / local op manager.
  * 
  * @param key identifier of the thread of the pool / local op manager
  * @return input buffer
  */
 IncomingBuffer getBuffer(byte key)
 {
  String skey = String.valueOf(key);	
  Object x = buffers.get(skey);
  if (x == null) x = lom_buffers.get(skey);
  return (IncomingBuffer) x; 
 }

 /**
  * Returns the input buffer of the current thread of the pool / local op manager; <code>null</code> is returned if 
  * the invoker is not a thread of the pool / local op manager.
  * 
  * @return input buffer
  */
 IncomingBuffer getBuffer()
 {
  return getBuffer(getThreadID());
 }

 /**
  * Returns all the input buffers as an array.
  * 
  * @return all the input buffers
  */
 IncomingBuffer[] getAllBuffers()
 {
  IncomingBuffer[] ib = new IncomingBuffer[buffers.size() + lom_buffers.size()];
  int k = 0;
  for (Enumeration e =     buffers.elements(); e.hasMoreElements();) ib[k++] = (IncomingBuffer) e.nextElement();
  for (Enumeration e = lom_buffers.elements(); e.hasMoreElements();) ib[k++] = (IncomingBuffer) e.nextElement();
  return ib;
 }
 
 /**
  * Returns an identifier of the current thread of the pool / local op manager. As a side effect it creates the 
  * incoming buffer for a local op manager.
  * 
  * @return identifier of the current thread
  */
 byte getThreadID()
 {
  byte x = conv_value;	
  String tname = Thread.currentThread().getName();
  
  try { x = Byte.parseByte(tname); }
  catch(NumberFormatException nfe) // the name is not a byte (i.e. worker thread).. local op manager 
  {
   Object z = lom_map.get(tname);
   if (z != null) x = Byte.parseByte((String) z); // this local op manager has already been assigned a buffer
      // otherwise create it
      else {  
      	    conv_counter++;
      	    byte auxbyte = (byte) (conv_counter + conv_value);
      	    lom_map.put(tname, String.valueOf(auxbyte));
      	    lom_buffers.put(String.valueOf(auxbyte), new IncomingBuffer());
      	    x = auxbyte;
           }
  }
  
  return x;
 }
 
}